#ifndef _I_DOUBLE_BUFFER_H_
#define _I_DOUBLE_BUFFER_H_

#include "../interface/I_Buffer.h"

class I_DoubleBuffer
{
protected:
	I_Buffer *m_buffer[2];

	int m_read, m_write;
	int m_size;

public:
	I_DoubleBuffer(void) 
	{ 
		m_size = 0;
		m_read = 0; 
		m_write = 1; 
		m_buffer[m_read] = m_buffer[m_write] = NULL; 
	}
	virtual ~I_DoubleBuffer(void) {}

	//////////////////////////////////////////////////////////////////////////
	/// I_DoubleBuffer knows how to create and release its pointers
	//////////////////////////////////////////////////////////////////////////
	virtual void				Create(int nSize, I_api_Device * pDevice = NULL, int nBuffer = e_Buffer_RW)
	{
		if (nBuffer & e_Buffer_R) CreateR(nSize, pDevice);
		if (nBuffer & e_Buffer_W) CreateW(nSize, pDevice);
	}

	virtual void				CreateR(int nSize, I_api_Device * pDevice = NULL)=0;
	virtual void				CreateW(int nSize, I_api_Device * pDevice = NULL)=0;

	virtual void        Create(int nSize, I_DoubleBuffer * pShared, int nBuffer = e_Buffer_RW)
	{
		if (nBuffer & e_Buffer_R) CreateR(nSize, pShared->pBufferR());
		if (nBuffer & e_Buffer_W) CreateW(nSize, pShared->pBufferW());
	}

	virtual	void				CreateR(int nSize, I_Buffer * pShared)=0;
	virtual	void				CreateW(int nSize, I_Buffer * pShared)=0;

	virtual void				Release(int32 nBuffer = e_Buffer_RW)
	{
		if (nBuffer & e_Buffer_R) ReleaseR();
		if (nBuffer & e_Buffer_W) ReleaseW();
	}

	virtual	void				ReleaseR(void) { m_buffer[m_read]->Release(); }
	virtual	void				ReleaseW(void) { m_buffer[m_write]->Release(); }

	virtual void			*	Map(int32 nFlag, int32 nBuffer)
	{
		if (nBuffer == e_Buffer_R) return MapR(nFlag);
		if (nBuffer == e_Buffer_W) return MapW(nFlag);
		return NULL;
	}

	virtual void			*	MapR(int32 nFlag) { return m_buffer[m_read]->Map(nFlag); }
	virtual void			*	MapW(int32 nFlag) { return m_buffer[m_write]->Map(nFlag); }

	virtual void				Unmap(int32 nBuffer)
	{
		if (nBuffer == e_Buffer_R) return UnmapR();
		if (nBuffer == e_Buffer_W) return UnmapW();
	}

	virtual void				UnmapR(void) { m_buffer[m_read]->Unmap(); }
	virtual void				UnmapW(void) { m_buffer[m_write]->Unmap(); }

	//////////////////////////////////////////////////////////////////////////
	/// In case one wants to get access to particular buffer pointers
	//////////////////////////////////////////////////////////////////////////
	virtual I_Buffer	* Buffer(int32 nBuffer) 
	{ 
		if (nBuffer == e_Buffer_R) return pBufferR();
		if (nBuffer == e_Buffer_W) return pBufferW();
		return NULL;
	}

	virtual I_Buffer	* pBufferR() { return m_buffer[m_read]; }
	virtual I_Buffer	* pBufferW() { return m_buffer[m_write]; }

	//////////////////////////////////////////////////////////////////////////
	/// I_DoubleBuffer knows how to copy data back and forth
	/// I_DoubleBuffer allows to some way to exchange data between pointers
	/// without any notion of what those pointers really are
	/// Copy direction DtoH copies from start index to stop device to host
	/// Copy direction HtoD copies from start index to stop host   to device
	/// buffer parameter can be either R or W or RW
	//////////////////////////////////////////////////////////////////////////
	virtual void				Memcpy(int32 nDir, int32 nStart = 0, int32 nCount = 0, int32 nBuffer = e_Buffer_RW)
	{
		if (nBuffer & e_Buffer_R) m_buffer[m_read]->Memcpy(nDir, nStart, nCount); 
		if (nBuffer & e_Buffer_W) m_buffer[m_write]->Memcpy(nDir, nStart, nCount); 
	}

	virtual void				Memset(void *pElem, int32 nStart = 0, int32 nCount = 0, int32 nBuffer = e_Buffer_RW)
	{
		if (nBuffer & e_Buffer_R) m_buffer[m_read]->Memset(pElem, nStart, nCount); 
		if (nBuffer & e_Buffer_W) m_buffer[m_write]->Memset(pElem, nStart, nCount); 
	}

	//////////////////////////////////////////////////////////////////////////
	/// Get host / device pointers for R or W buffers
	//////////////////////////////////////////////////////////////////////////
	virtual void			* hPtr(int32 nBuffer)		
	{
		if (nBuffer == e_Buffer_R) return hPtrR();
		if (nBuffer == e_Buffer_W) return hPtrW();
		return NULL;
	}

	virtual void			* hPtrR(void)		{ return m_buffer[m_read]->hPtr(); }
	virtual void			* hPtrW(void)		{ return m_buffer[m_write]->hPtr(); }

	virtual void			* dPtr(int32 nBuffer)
	{
		if (nBuffer == e_Buffer_R) return dPtrR();
		if (nBuffer == e_Buffer_W) return dPtrW();
		return NULL;
	}

	virtual void			* dPtrR(void)		{ return m_buffer[m_read]->dPtr(); }
	virtual void			* dPtrW(void)		{ return m_buffer[m_write]->dPtr(); }

	virtual void      * bufferPtr(int32 nBuffer)	
	{
		if (nBuffer == e_Buffer_R) return bufferPtrR();
		if (nBuffer == e_Buffer_W) return bufferPtrW(); 
		return NULL;
	}

	virtual void      * bufferPtrR(void)	{return m_buffer[m_read]->bufferPtr(); }
	virtual void      * bufferPtrW(void)	{return m_buffer[m_read]->bufferPtr(); }

	virtual int					Offset(int32 nBuffer)
	{
		if (nBuffer == e_Buffer_R) return OffsetR();
		if (nBuffer == e_Buffer_W) return OffsetW();
		return -1;
	}

	virtual int					OffsetR() { return m_buffer[m_read]->Offset(); }
	virtual int					OffsetW() { return m_buffer[m_write]->Offset(); }

	virtual bool				Owner(int32 nBuffer)
	{
		if (nBuffer == e_Buffer_R) return OwnerR();
		if (nBuffer == e_Buffer_W) return OwnerW();
		return false;
	}

	virtual bool				OwnerR() { return m_buffer[m_read]->Owner(); }
	virtual bool				OwnerW() { return m_buffer[m_write]->Owner(); }

	//////////////////////////////////////////////////////////////////////////
	/// I_DoubleBuffer can swap R and W buffers places
	//////////////////////////////////////////////////////////////////////////
	virtual void        Swap(void)
	{
		int save = m_write;
		m_write = m_read;
		m_read = save;
	}

	//////////////////////////////////////////////////////////////////////////
	/// Size returns size in elements wheres SizeInBytes is in bytes
	//////////////////////////////////////////////////////////////////////////
	virtual bool        isValidSize(void) { return m_buffer[m_read]->Size() == m_buffer[m_write]->Size(); }
	virtual int         Size(void) { if (isValidSize()) return m_buffer[m_read]->Size(); else return -1; }
};

#endif